<?php
require_once 'RL/Interface.php';
require_once 'RL/ConfigProcessor.php';
require_once 'RL/Exception.php';

/**
 * Абстрактный класс-обработчик запроса
 *
 * @abstract 
 * @package mumak
 * @subpackage core
 */
abstract class RL_BasicRequestHandler extends RL_ConfigProcessor
{

	/**
	 * Корневой URL проекта http://server/site/
	 *
	 * @var string
	 */
	public $base_url;

	/**
	 * Дирректоря сайта на сервере /site/
	 *
	 * @var string
	 */
	public $base_path;

	/**
	 * Базовая папка для файлов проекта
	 *
	 * @var string
	 */
	public $base_dir;

	/**
	 * Корневой URL сервера проекта (с портом, если не 80)
	 *
	 * @var string
	 */
	public $server_url;

	/**
	 * Корневой домен
	 *
	 * @var string
	 */
	public $base_domain;

	/**
	 * Текущий домен
	 *
	 * @var string
	 */
	public $current_domain;

	/**
	 * Домен для cookies
	 *
	 * @var string
	 */
	public $cookie_domain;

	/**
	 * Контекст приложения
	 *
	 * @var RL_Context
	 */
	public $ctx;

	// -- constructor -- {{{
	/**
	 * Собственный обработчик ошибок
	 *
	 * @abstract 
	 * @param int $errno
	 * @param string $errstr
	 * @param string $errfile
	 * @param int $errline
	 * @param unknown_type $errcontext
	 * @param unknown_type $error_types
	 */
	abstract public function debugErrorHandler ($errno, $errstr, $errfile, $errline, $errcontext = NULL, $error_types = NULL);
	private function setPaths ()
	/**
	 * Устанавливает параметры директорий
	 * 
	 * @return void
	 */
	{
		if (!isset($this->server_host) || !isset($this->server_port)) {
			$server = explode(':', $_SERVER['HTTP_HOST'], 2);
			if (count($server) == 2) {
				$server_host = $server[0];
				$server_port = intval($server[1]);
			} else {
				$server_host = $server[0];
				$server_port = 80;
			}
			if (!isset($this->server_host)) { $this->server_host = $server_host; }
			if (!isset($this->server_port)) { $this->server_port = $server_port; }
		}
		if (!isset($this->server_schema)) {
			$this->server_schema 
				= strtolower(substr($_SERVER['SERVER_PROTOCOL'], 0, strpos($_SERVER['SERVER_PROTOCOL'], '/')));
		}
		// вычисляем base_url
		if (!isset($this->server_url) ) {
			$this->server_url = 
				$this->server_schema . '://'.$this->server_host. ($this->server_port === 80 ? '' : ':'.$this->server_port);
		}
		if( !isset($this->base_path) ) {
			$this->base_path = dirname($_SERVER["PHP_SELF"]).( dirname($_SERVER["PHP_SELF"])!='/' ? '/' : '' );
		}
		if( !isset($this->base_url) ) {
			$this->base_url =	$this->server_url . $this->base_path;
		}
		if( !isset($this->base_dir) ) {
			$this->base_dir = $_SERVER["DOCUMENT_ROOT"].$this->base_url;
		}
	}
	private function setDomains ()
	/**
	 * Устанавливает конфигурационные параметры доменов
	 *
	 * @return void
	 */
	{
		// устанавливаем домены
		if (!isset($this->base_domain)) {
			$this->base_domain = preg_replace("/^www\./i", "", $_SERVER["SERVER_NAME"]);
		}
		if (!isset($this->current_domain)) {
			$this->current_domain = preg_replace("/^www\./i", "", $_SERVER["HTTP_HOST"]);
		}
		if (!isset($this->cookie_domain)) {
			// lucky@npj: see http://ru.php.net/manual/ru/function.setcookie.php#49350
			// FIXME : 2008-01-23 : lucky
			// -- документировать --
			// когда $this->cookie_domain = false , то кука доступна лишь домену, с которого был запрос.
			// когда $this->cookie_domain содержит имя домена (с точкой в начале или без),
			// то кука отдается доменам верхних уровней.
			// для доменов типа 'localhost' $this->cookie_domain = false должен быть всегда.
			$this->cookie_domain = strpos($this->base_domain, '.') === false 
				? false 
				: // сделать куку доступной для всех поддоменов $this->base_domain
				"." .  $this->base_domain;
		}
		if (!isset($this->cookie_prefix)) {
			// по если не пусто, используем имя проекта (как универсальный префикс для всех имен).
			// иначе, 
			// если $this->cookie_domain false, то $ths->cookie_prefix можно оставить пустым, т.к.
			// куки больше нигде не видны.
			// иначе,
			// используем имя хоста в качестве префикса.
			$this->cookie_prefix = $this->project_name ? $this->project_name : 
				($this->cookie_domain ? strtr($this->base_domain, array('.' => '_')): '');
		}

		session_set_cookie_params(0, "/", $this->cookie_domain);
		// закончили с доменами
	}
	/**
	 * Создание рабочего окружения
	 *
	 * @abstract 
	 * @return RL_Context
	 */
	abstract protected function createContext ();

	final public function __construct( $config )
	/**
	 * Непереопределяемый конструктор
	 *
	 * @final 
	 * @param mixed $config либо путь к файлу, либо объект
	 */
	{
		// устанавливаем наш обработчик ошибок
		set_error_handler(array(&$this, 'debugErrorHandler'));

		if (@is_readable ( $config ) ) {
			$self = array();
			require_once($config);
			foreach ($self as $k=>$v) {
				$this->$k = $v;
			}
			$this->setPaths(); // устанавливаем параметры директорий

			$this->setDomains(); // устанавливаем домена

			//$this->fuckAllQuotes(); // удаляем кавычки
			$this->ENV = $_ENV;
			$this->SERVER = $_SERVER;

			// теперь тут создание рабочего окружения
			$this->useClass("RL_Context");
			$this->ctx = $this->createContext();
			if (isset($this->ctx->debug)) $this->debug = $this->ctx->debug;
		} else {
			error(__METHOD__.' Cannot read local configurations.');
		}
	}
	// -- constructor -- }}}


	// -- handle request workflow -- {{{
	/**
	 * Инициализация объекта
	 *
	 * @abstract 
	 * @return void
	 */
	abstract public function initialize();

	/**
	 * Главная функция, которую надо перегрузить в потомках! Обработка запроса
	 *
	 * @abstract 
	 * @return mixed
	 */
	abstract public function handleRequest ();

	/**
	 * Завершение работы RequestHandler'а
	 *
	 * @return void
	 */
	abstract public function finalize ();
	/**
	 * Штатное завершение работа
	 *
	 * @return mixed
	 */
	abstract public function end();
	// -- handle request workflow -- }}}


	public function useClass( $name, $level = 0, $dr = 1, $ext = 'php' )
	/**
	 * Подключить класс
	 *
	 * @param string $name
	 * @param int $level
	 * @param int $dr
	 * @param string $ext
	 */
	{
		return $this->useScript("/.." , str_replace('_', '/',$name) , $level , $dr , $ext);
	}
	public function useLib( $library_name, $file_name = "" )
	/**
	 * Подключить библиотеку
	 *
	 * @param string $library_name имя библиотеки
	 * @param string $file_name название файла, если оно отличается от названия библиотеки
	 */
	{
		// library is near core, library have no levels
		//$direction = 0;
		// lucky@npj: фиг вам -- где угодно. сначала в приложении, затем в core
		$direction = 1;
		$level = 0; 
		// usually library have one file to link itself
		if ($file_name == "") {
			$file_name = $library_name; 
		}
		$ext = "php";

		$this->useScript( $this->lib_dir, $library_name."/".$file_name, $level, $direction, $ext);
	}

	public function error ( $message )
	/**
	 * Выкидыш ошибки
	 *
	 * @param string $message
	 */
	{
		trigger_error( $message , E_USER_ERROR );
	}
}

